/**
* @brief
*		Find errors and decrease probability of getting errors of the same kind in the future
*		This piece of code won't compile and it doesn't describe an entire algorithm: just part of some page storage
*
* @author
*		AnnaM
*/

//15 bugs

#include <Windows.h>
#include <stdio.h>

enum PAGE_COLOR
{
	PG_COLOR_GREEN = 0, /* page may be released without high overhead */
	PG_COLOR_YELLOW, /* nice to have */
	PG_COLOR_RED	/* page is actively used */
	//весьма опасно, особенно если кто-то догадается явно указать какое-то значение в дальнейшем или добавить значение после PG_COLOR_RED
	PG_COLOR_NONE //Вариант - просто добавить маркет конца, тогда индексацию необходимо начинать с нуля
	//Однако не спасает от явного указания значений для элементов enum-а
	
};
//Вариант2 - использовать конечный набор с маркером конца. Единственная проблема - необходимо чтобы в  PAGE_COLOR явно не указывались значения
typedef struct PageType {
	PAGE_COLOR color;
	const char *name;
	PageDesc* desc_list;
} PageType_t;

#define PG_COLOR_TYPE_INIT(clr) {clr, #clr, NULL} //защита от {PG_COLOR_RED, PG_COLOR_NAME(PG_COLOR_GREEN)}
static PageType_t PageTypes[] = 
	{
		PG_COLOR_TYPE_INIT(PG_COLOR_GREEN),
		/*{....},*/
		{PG_COLOR_NONE, NULL, NULL} //маркер конца
	};
#undef PG_COLOR_TYPE_INIT
static int PageTypesLen = sizeof(PageTypes) / sizeof(PageType_t);//ровно столько, склько нужно, чтобы вместить
/**
 * UINT Key of a page in hash-table (prepared from color and address)
 */
union PageKey
{
	struct
	{
        CHAR	cColor: 8;
		UINT	uAddr: 24; //Видимо, перепутано имя, на это указывает тип и последующие обращения к полю
	};//в 64-битной винде адрес страницы должен состоять из 52-х бит, если размер страницы - 4Кб

	UINT	uKey;
};


/* Prepare from 2 chars the key of the same configuration as in PageKey */
#define CALC_PAGE_KEY( Addr, Color )	(  (Color) + ((Addr) << 8) ) //у + приоритет выше, чем у <<


/**
 * Descriptor of a single guest physical page
 */
struct PageDesc
{
	PageKey			uKey;	

	/* list support */
	PageDesc		*next, *prev; //prev всегда NULL
};

#define PAGE_INIT( Desc, Addr, Color )              \
    do {                                            \
        (Desc).uKey = CALC_PAGE_KEY( Addr, Color ); \ //здесь значение CALC_PAGE_KEY урежется, void* может быть 64-битным
        (Desc).next = (Desc).prev = NULL;           \
    } while(0); //от многострочного макроса ожидается поведение функции

/*
static PageDesc* PageStrg[ PageStrgLen ]; //теперь можно убрать магическое число
*/

//массив больше не нужен
/*
void PageStrgInit()
{
	memset( PageStrg, 0, sizeof(PageStrg) ); //обнулятся только sizeof(&PageStrg) = 4/8 байт, правильно sizeof(PageStrg) - 12/24 байта
}
*/
PageDesc* PageFind( void* ptr, char color )
{
	UINT key = CALC_PAGE_KEY(ptr,color);
	//незачем вычислять это значение на каждом шаге
	PageType_t* cur_page_type = PageTypes;
	//небольшие накладные расходы на поиск. 
	//Можно заменить на Pg = PageTypes[color].desc_list, если известно, что цвета идут с 0 строго по порядкe
	while( cur_page_type->name ) 
	{
		if(cur_page_type->color == color)
			break;
		cur_page_type++;
	}
	if(!cur_page_type)
		return NULL;	
	for( PageDesc* Pg = cur_page_type->desc_list; Pg; Pg = Pg->next );
        if( Pg->uKey == key )
		   return Pg;                                                                                                                                     
    return NULL;
}

/*
PageDesc* PageReclaim( UINT cnt )
{
	UINT color = 0;
	//PageDesc* Pg; //неинициализированный указатель
	PageDesc* Pg;
	while( cnt )
	{
		Pg = Pg->next; //из мусора в мусор
		//Сразу берется следующий элемент, первый элемент никогда не удалится
		PageRemove( PageStrg[ color ] );
		cnt--;
		if( Pg == NULL ) //поздно проверять данные
		{
			color++;
			Pg = PageStrg[ color ];
		}
	}//если cnt больше кол-ва всех страниц - пойдем смотреть мусор
}
*/
//Исправлял функцию исходя из того, что страница должна удалиться из PageStrg после ей удаления и что используется только next
void PageReclaim( UINT cnt )//возвращаемое значение всегда NULL
{
	PageType_t* cur_page_type = PageTypes;
	PageDesc* Pg;
	while( cnt && cur_page_type->name )
	{
		Pg = cur_page_type->desc_list;
		if( Pg == NULL ) //проверка на наличие данных до операций над ними
		{
			cur_page_type++;
			continue;
		}
		cur_page_type->desc_list = cur_page_type->desc_list->next;
		PageRemove( Pg );
		cnt--;
	}
}


PageDesc* PageInit( void* ptr, UINT color )
{
	try {
		PageDesc* pg = new PageDesc; 
		//new генерирует исключение при невозможности выделить память, возвращаемого значения в этом случае мы не дождемся, вылетим с исключением
	}
	catch (std::bad_alloc& e) { 
		printf("Allocation has failed\n");
		return NULL;
	}
	PAGE_INIT(*pg, ptr, color) 
	//PAGE_INIT ожидает передачу аргумента по значинию, передается двойной указатель.
    return pg;
}


/**
 * Print all mapped pages
 */
void PageDump()
{
	/*
	#define PG_COLOR_NAME(clr) #clr
	const char* PgColorName[] = //строки должны быть константными
	{
		PG_COLOR_NAME(PG_COLOR_GREEN),
		PG_COLOR_NAME(PG_COLOR_YELLOW),
		PG_COLOR_NAME(PG_COLOR_RED) //был перепутан порядок цветов страниц
	};
	*/
	//while( color < PG_COLOR_RED )//PG_COLOR_RED = 3  - color станет равным 4, PageStrg[color], PgColorName[color] станет указывать на мусор
	PageType_t* cur_page_type = PageTypes;
	while( cur_page_type->name )
	{
		//printf("PgStrg[(%u) %s] ********** \n", color, PgColorName[color] ); //были перепутаны аргументы форматирования
		printf("PgStrg[(%u) %s] ********** \n", cur_page_type->color, cur_page_type->name ); //были перепутаны аргументы форматирования
		for( PageDesc* Pg = PageStrg[cur_page_type->color]; Pg != NULL; Pg = Pg->next )//никогда не возьмет 0-ой элемент, правильно PageStrg[color++]
		{
			if( Pg->uAddr == NULL )//присвоение вместо сравнения
				continue;

			printf("Pg :Key = 0x%x, addr %p\n", Pg->uKey, Pg->uAddr );//"%I64u" для винды если UINT 64-битный.
			//странные обращения к полям объединения PageKey, при том, что Pg это указатель на PageDesc
			//может быть Pg->uKey.uKey?
		}
		cur_page_type++;
	}
	//#undef PG_COLOR_NAME //возможно, это имя понадобится выше в другом контексте, лучше раздефайнить его сразу после использования по назначению
}
